package com.xinlianshiye.im.dao.impl;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQuery;
import com.xinlianshiye.im.dao.BaseRepository;
import com.xinlianshiye.im.dao.custom.RecordDaoCustom;
import com.xinlianshiye.im.response.UserRecordlist;
import com.xinlianshiye.im.bean.QFriend;
import com.xinlianshiye.im.bean.QRecord;
import com.xinlianshiye.im.bean.Record;
import com.xinlianshiye.im.bean.User;
import com.xinlianshiye.im.response.Page;
import org.springframework.util.StringUtils;

public class RecordDaoImpl extends BaseRepository implements RecordDaoCustom {
    @Override
    public Page<UserRecordlist> findUseridRecordCustom(Integer userid, String search) {
        QRecord record = QRecord.record;
        QFriend friend = QFriend.friend1;
        List<Integer> list = new ArrayList<>();
        JPAQuery<Tuple> query = queryFactory.select(record.otherId, record.createTime.max()).from(record).where(record.owner.id.eq(userid));
        if (StringUtils.hasText(search)) {
            query.where(record.content.contains(search).or(record.other.username.contains(search)));
        }
        query.groupBy(record.otherId).fetch().forEach(tuple -> {
            List<Integer> con = queryFactory.select(record.id)
                    .from(record)
                    .where(record.otherId.eq(tuple.get(record.otherId)))
                    .where(record.createTime.eq(tuple.get(record.createTime.max())))
                    .groupBy(record.otherId)
                    .fetch();
            list.addAll(con);
        });
        JPAQuery<Record> recordResult = queryFactory.select(record)
                .from(record).where(record.id.in(list)).where(record.deleted.eq(false));
        recordResult.orderBy(record.createTime.desc());
        Integer recirdCount = (int) recordResult.fetchCount();
        List<Record> rd = recordResult
                .offset(0).limit(30).fetch();
        List<UserRecordlist> listuser = new ArrayList<>();
        rd.forEach(record1 -> {
            UserRecordlist userRecordlist = new UserRecordlist();
            userRecordlist.setId(record1.getId());
            userRecordlist.setContent(record1.getContent());
            userRecordlist.setCreated(Date.from(record1.getCreateTime()));
            userRecordlist.setFromId(record1.getOwnerId());
            userRecordlist.setIssend(record1.isSender());
            userRecordlist.setState(record1.getHaveRead());
            userRecordlist.setToId(record1.getOtherId());
            Boolean friendsexists = queryFactory.select(friend.id).from(friend).where(friend.user.id.eq(record1.getOwnerId())).where(friend.friend.id.eq(record1.getOtherId())).fetchCount() > 0;
            userRecordlist.setExistsfriends(friendsexists);
            User touser = new User();
            User user = record1.getOther();
            touser.setId(user.getId());
            touser.setUsername(user.getUsername());
            touser.setAvatar(user.getAvatar());
            userRecordlist.setTo(touser);
            userRecordlist.setType(record1.getType());
            long num = queryFactory.select(record).from(record).where(record.owner.id.eq(record1.getOwnerId())).where(record.other.id.eq(record1.getOtherId())).where(record.haveRead.eq(false)).where(record.isSender.eq(false)).fetchCount();
            Integer count = Integer.valueOf(String.valueOf(num));
            userRecordlist.setNoreadcount(count);
            listuser.add(userRecordlist);
        });
        Integer noRead = listuser.stream().mapToInt(UserRecordlist::getNoreadcount).sum();
        return new Page(listuser, 0, 30, recirdCount, noRead);
    }

    @Override
    public Integer count(Integer ownerId, Integer otherId) {
        QRecord record = QRecord.record;
        long count = queryFactory
                .selectFrom(record)
                .where(record.ownerId.eq(ownerId))
                .where(record.otherId.eq(otherId))
                .where(record.deleted.eq(false))
                .fetchCount();
        return Long.valueOf(count).intValue();
    }

    @Override
    public List<Record> findAllUnRead(Integer userId, Integer other) {
        QRecord record = QRecord.record;
        List<Record> list = new ArrayList<>();
        queryFactory.select(record.id, record.createTime, record.type, record.haveRead, record.content, record.owner.id, record.other.id, record.isSender)
                .from(record)
                .where(record.owner.id.eq(userId))
                .where(record.other.id.eq(other))
                .where(record.haveRead.eq(false))
                .where(record.isSender.eq(false))
                .where(record.deleted.eq(false)).fetch().forEach(tuple -> {
            Record rd = new Record();
            rd.setId(tuple.get(record.id));
            rd.setCreateTime(tuple.get(record.createTime));
            rd.setSender(tuple.get(record.isSender));
            rd.setType(tuple.get(record.type));
            rd.setHaveRead(tuple.get(record.haveRead));
            rd.setContent(tuple.get(record.content));
            rd.setOwnerId(tuple.get(record.owner.id));
            rd.setOtherId(tuple.get(record.other.id));
            list.add(rd);
        });
        return list;
    }

    @Override
    public Integer readRecord(Integer ownerId, Integer otherId) {
        QRecord record = QRecord.record;
        long n1 = queryFactory.update(record)
                .set(record.haveRead, true)
                .where(record.owner.id.eq(ownerId))
                .where(record.other.id.eq(otherId))
                .where(record.haveRead.eq(false))
                .where(record.isSender.eq(false))
                .execute();
        long n2 = queryFactory.update(record)
                .set(record.haveRead, true)
                .where(record.owner.id.eq(otherId))
                .where(record.other.id.eq(ownerId))
                .where(record.haveRead.eq(false))
                .where(record.isSender.eq(true))
                .execute();
        return Long.valueOf(n1).intValue();
    }

    /**
     * 查询与目标用户的第一条和最后一条聊天消息的发送日期
     */
    @Override
    public List<LocalDate> findDurationDate(Integer ownerId, Integer otherId) {
        QRecord record = QRecord.record;
        Instant first = queryFactory
                .select(record.createTime)
                .from(record)
                .where(record.owner.id.eq(ownerId))
                .where(record.other.id.eq(otherId))
                .orderBy(record.createTime.asc())
                .fetchFirst();
        Instant last = queryFactory
                .select(record.createTime)
                .from(record)
                .where(record.owner.id.eq(ownerId))
                .where(record.other.id.eq(otherId))
                .orderBy(record.createTime.desc())
                .fetchFirst();
        List<LocalDate> duration = new ArrayList<>();
        duration.add(first.atZone(ZoneId.systemDefault()).toLocalDate());
        duration.add(last.atZone(ZoneId.systemDefault()).toLocalDate());
        return duration;
    }

    /**
     * 分页查询聊天记录
     * 1. 有关键字的时候 就不需要判断日期了
     * 2. 如果没有关键字 有开始日期 就搜索开始日期之后的聊天记录
     */
    @Override
    public Page<Record> page(Integer ownerId, Integer otherId, String keyword, LocalDate startDate, Integer start, Integer limit) {
        QRecord record = QRecord.record;
        JPAQuery<Record> query = queryFactory
                .selectFrom(record)
                .where(record.ownerId.eq(ownerId).and(record.otherId.eq(otherId)))
                .where(record.deleted.eq(false))
                .orderBy(record.createTime.asc());
        if (StringUtils.hasText(keyword)) {
            query.where(record.content.contains(keyword));
        } else if (startDate != null) {
            query.where(record.createTime.after(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
        }
        long totalCount = query.fetchCount();
        if (start == null) {
            // 如果start为空， 则从最后分页的最后一页开始查询, 计算start为最后一页的开始记录数
            start = limit * ((int) Math.ceil((totalCount * 1.0) / limit) - 1);
            if (start < 0) {
                start = 0;
            }
        }
        List<Record> list = query.offset(start).limit(limit).fetch();
        return new Page<>(list, start, limit, (int) totalCount);
    }
}
